#include "Tournament.h"
#include "RandomNumberGenerator.h"
#include "Crossover.h"

Tournament::Tournament(float selectionProb, float mutationRate, int mutationAmount,Crossover* crossover)
{
    this->selectionProb = selectionProb;
    this->evolve = crossover;
    this->mutationRate = 0.005;
    this->mutationAmount = mutationAmount;
    this->elitism = 5;
    this->increment = 0;
    //this->mutationRate = mutationRate;
}
Tournament::~Tournament() {
    delete this->evolve;
}

std::vector<Candidate*> Tournament::select(std::vector<Candidate*> population, int selectionSize) {
   result.clear();
   while(result.size() < (population.size())) {
        Candidate* parent1 = fight(
                population.at(RandomNumberGenerator::nextInt(population.size() -1)),
                population.at(RandomNumberGenerator::nextInt(population.size() -1))
                );
        Candidate* parent2 = fight(
                population.at(RandomNumberGenerator::nextInt(population.size() -1)),
                population.at(RandomNumberGenerator::nextInt(population.size() -1))
                );

        std::vector<Candidate*> offSpring = evolve->apply(parent1,parent2);
        result.push_back(mutate(offSpring.at(0)));
        result.push_back(mutate(offSpring.at(1)));
    }

    if(result.size() != population.size()) {
        result.push_back(population.at(0));
    }
    return result;
}
void Tournament::clearResult() {
    result.clear();
}

void Tournament::incrementSelect(std::vector<Candidate*> population, int epochSize) {

    for(int i=0;i<epochSize;i++) {
        Candidate* parent1 = fight(
                population.at(RandomNumberGenerator::nextInt(population.size())),
                population.at(RandomNumberGenerator::nextInt(population.size()))
                );
        Candidate* parent2 = fight(
                population.at(RandomNumberGenerator::nextInt(population.size())),
                population.at(RandomNumberGenerator::nextInt(population.size()))
                );

        std::vector<Candidate*> offSpring = evolve->apply(parent1,parent2);
        result.push_back(mutate(offSpring.at(0)));
        result.push_back(mutate(offSpring.at(1)));
        increment++;
    }
}

std::vector<Candidate*> Tournament::getResult(std::vector<Candidate*> population,int populationSize) {
    while(result.size() != populationSize) {
        if(result.size() < populationSize) {
            result.push_back(population.at(RandomNumberGenerator::nextInt(populationSize)));
        } else {
            result.erase(result.begin()+RandomNumberGenerator::nextInt(result.size()));
        }

    }
    return result;
}

Candidate* Tournament::fight(Candidate* tp1, Candidate* tp2) {
    if(tp1->getFitness() < tp2->getFitness()) {
        if(selectionProb > RandomNumberGenerator::nextFloat(1)) {
            return tp1;
        } else {
            return tp2;
        }
    } else {
        if(selectionProb > RandomNumberGenerator::nextFloat(1)) {
            return tp2;
        } else {
            return tp1;
        }
    }
}

Candidate* Tournament::mutate(Candidate* candidate) {
    candidate->mutate(mutationRate,mutationAmount);
    return candidate;
}
void Tournament::setMutationRate(float mutationRate) {
    this->mutationRate = mutationRate;
}
